#ifndef __CZF_SIMPLE_WIN_H__
#define __CZF_SIMPLE_WIN_H__

#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif

#define _WIN32_WINNT 0x0501

#include <windows.h>
#include <windowsx.h>
#include <assert.h>
#include <tchar.h>
#include <commctrl.h>

#ifdef DEBUG
#include <stdio.h>
#include <stdarg.h>
#define _TRACE MyTrace
void MyTrace(LPCTSTR format, ...);
#else
#define _TRACE __noop
#endif

LRESULT CALLBACK HookThunk (HWND, UINT, WPARAM, LPARAM); 
int CALLBACK HookDlgThunk (HWND, UINT, WPARAM, LPARAM); 

#define DEFAULT_CLASS_STYLE (CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS)
#define NO_BACKGROUND   (-1)

#define DECLARE_WND_CLASS_EX(name,style0,background) \
    virtual WNDCLASSEX* GetWndClassInfo() \
    {   \
        static WNDCLASSEX wc; \
        wc.cbSize = sizeof (WNDCLASSEX);    \
        wc.style = style0; \
        wc.lpfnWndProc = HookThunk; \
        wc.cbClsExtra = 0;  \
        wc.cbWndExtra = 0;  \
        wc.hInstance = _Module.GetModuleInstance();   \
        wc.hIcon = NULL;    \
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);  \
        wc.hbrBackground = (HBRUSH)(background+1);  \
        wc.lpszMenuName = NULL; \
        wc.lpszClassName = name; \
        wc.hIconSm = NULL;  \
        return &wc;    \
    }

#define DECLARE_WND_SUPERCLASS(me,base) \
    virtual WNDCLASSEX* GetWndClassInfo() \
    { \
        static WNDCLASSEX wc;   \
        BOOL bRet = GetClassInfoEx (NULL, base, &wc);     \
        if (!bRet) {    \
            GetClassInfoEx (_Module.GetModuleInstance(), base, &wc);    \
        }   \
        m_pfnDefault = wc.lpfnWndProc;  \
        wc.cbSize = sizeof(WNDCLASSEX); \
        wc.lpfnWndProc = HookThunk; \
        wc.hInstance = _Module.GetModuleInstance(); \
        wc.lpszClassName = me;  \
        return &wc; \
    }

#pragma pack(push,1)
/* Construct piece of code, replace the hWnd with pThis*/
typedef struct 
{
	DWORD   m_mov;          // mov dword ptr [esp+0x04], pThis (esp+0x04 is hWnd)
	DWORD   m_pThis;   
	BYTE    m_jmp;          // relative jmp 
	DWORD   m_relproc;
} WndProcThunk;
#pragma pack(pop)

class CWndProcThunk
{
public:
    WndProcThunk   m_thunk;

	void Init(void* theProc, void* pThis);
};


#define BEGIN_MSG_MAP(cppclass) \
    virtual LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL& bHandled)\
    { \
        assert (hWnd == m_hWnd); \
        LRESULT ret = 0; \
        bHandled = false;


#define MESSAGE_HANDLER(msg,func) \
        if (message == msg) { \
            ret = func(message, wParam, lParam, bHandled); \
            if (bHandled) { \
                return ret; \
            }   \
        }

#define COMMAND_ID_HANDLER(id,func) \
        if (message == WM_COMMAND && id == LOWORD(wParam)) { \
            ret = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled);    \
            if (bHandled) { \
                return ret; \
            }   \
        }

#define NOTIFY_ID_HANDLER(id,func) \
	    if (message == WM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom) { \
		    ret = func((int)wParam, (LPNMHDR)lParam, bHandled); \
            if (bHandled) { \
			    return ret; \
            } \
        }

#define NOTIFY_CODE_HANDLER(xxcode,func) \
	    if (message == WM_NOTIFY && xxcode == ((LPNMHDR)lParam)->code) { \
		    ret = func((int)wParam, (LPNMHDR)lParam, bHandled); \
		    if (bHandled) {\
			    return ret; \
          } \
       }

#define NOTIFY_RANGE_HANDLER(idFirst,idLast,func) \
	    if(message == WM_NOTIFY && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) { \
		    ret = func((int)wParam, (LPNMHDR)lParam, bHandled); \
            if (bHandled) { \
			    return ret; \
            }\
	    }
// The CHIAN_MSG_MAP has to be after all those handlers
#define CHAIN_MSG_MAP(theChainClass)  \
      if (!bHandled) { \
	      ret = (theChainClass::WndProc(hWnd, message, wParam, lParam, bHandled)); \
         if (bHandled) { \
            return ret; \
         } \
      }

#define END_MSG_MAP()   \
         return ret; \
      }

/* Hopefully, we will need less than 1024 UI threads. */
#define MAX_THREADS 1024

class CWnd;

typedef struct tagWndThisPtr
{
    struct tagWndThisPtr* pNext;
    DWORD dwThreadID;
    CWnd* pThis;
} WndThisPtr;

class CModule
{
public:
   CModule();
   virtual ~CModule();

   void Init(HINSTANCE hInst);
   HINSTANCE GetModuleInstance();
   BOOL InitCommonControls(); 

   void AddClassPointer(CWnd* pThis);
   CWnd* GetClassPointer();


public:
   HINSTANCE           m_hInstance;
   WndThisPtr*         m_pClasses;
   HANDLE              m_hMutex;
};

extern CModule _Module;
 
class CWnd
{
protected:
   CWnd();
   virtual ~CWnd();

public:    
   DECLARE_WND_CLASS_EX(_T("CZF_Simple_Wnd_Base"), DEFAULT_CLASS_STYLE, COLOR_WINDOW)
   
   HWND Create (HWND hWndParent, 
             const RECT& rcPos, 
             LPCTSTR szWindowName = NULL, 
             DWORD dwStyle = WS_CHILD | WS_VISIBLE, 
             DWORD dwExStyle = 0, 
             UINT nID = 0 );
   
   
   BEGIN_MSG_MAP(CWnd)
   END_MSG_MAP()
   
   virtual void OnFinalMessage(HWND hWnd);
   
   WNDPROC InitThunk(void* theProc, bool bIsDlg=false);
   void Attach(HWND hWnd);

public:
   /*Wrappers*/
   HWND GetParent();
   BOOL GetClientRect(LPRECT lpRect);
   BOOL GetWindowRect(LPRECT lpRect);
   BOOL ScreenToClient(LPPOINT lpPoint);
   BOOL ScreenToClient(LPRECT lpRect);
   BOOL ClientToScreen(LPPOINT lpPoint);
   BOOL ClientToScreen(LPRECT lpRect);
   int GetScrollPos(int nBar);
   int SetScrollPos(int nBar, int nPos, BOOL bRedraw=TRUE);
   BOOL GetScrollRange(int nBar, LPINT lpMinPos, LPINT lpMaxPos);
   BOOL SetScrollRange(int nBar, int nMinPos, int nMaxPos, BOOL bRedraw=TRUE);
   BOOL ShowScrollBar(int nBar, BOOL bShow=TRUE);
   
   BOOL Invalidate(BOOL bErase = TRUE);
   BOOL InvalidateRect(LPCRECT lpRect, BOOL bErase = TRUE);
   BOOL MoveWindow(int X, int Y, int nWidth, int nHeight, BOOL bRepaint=TRUE);
   BOOL MoveWindow(LPCRECT lpRect, BOOL bRepaint = TRUE );
   
   HDC GetDC();
   int ReleaseDC(HDC dc);
   HDC BeginPaint(LPPAINTSTRUCT lpPaint);
   BOOL EndPaint(CONST PAINTSTRUCT *lpPaint);
   
   HWND SetFocus();
   BOOL IsWindow();
   BOOL HideCaret();
   BOOL ShowCaret();
   BOOL CreateSolidCaret(int nWidth, int nHeight);
   BOOL CreateCaret(HBITMAP hBitmap, int nWidth, int nHeight);
   HWND SetCapture();
   
   BOOL OpenClipboard();
   
   BOOL UpdateWindow();
   BOOL ShowWindow(int nCmdShow);
   BOOL DestroyWindow();
   BOOL CenterWindow(HWND hWndCenter = NULL);

public:
   HWND                m_hWnd;
   WNDPROC             m_pfnDefault;
   CWndProcThunk       m_thunk;
};

#define DIALOG_ID(x) virtual DWORD GetDialogID() {return x;};

class CDialog : public CWnd
{
protected:
   CDialog();
   virtual ~CDialog();
   
   DIALOG_ID(0)
    
public:
   HWND Create( HWND hWndParent, LPARAM dwInitParam = NULL );
   int DoModal( HWND hWndParent = ::GetActiveWindow( ) );
   
   HWND GetDlgItem(int nIDDlgItem);
   BOOL EndDialog(int nResult);
   
   BOOL CheckRadioButton(int nIDFirstButton, int nIDLastButton,  int nIDCheckButton);
   void NextDlgCtrl();
   void PrevDlgCtrl();
   UINT IsDlgButtonChecked(int nIDButton);
};

#endif /* __CZF_SIMPLE_WIN_H__ */
